package com.peng.config;

import com.peng.mapper.UserMapper;
import com.peng.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.Authentication;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.sql.DataSource;
import java.io.IOException;

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private SecurityLoginUser securityLoginUser;
    @Autowired
    private MyAccessDeniedHandler myAccessDeniedHandler;
    @Autowired
    private PersistentTokenRepository persistentTokenRepository;

    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        /**
         * 授予权限的方法逻辑 常用方法：
         *  permitAll() 免登录
         *  anonymous() 匿名访问，登录后不能访问
         *  authenticated() 登录后可以访问
         *  denyAll()   任意用户，任意状态都不可访问
         *  fullyAuthenticated()    完整登录才可以访问。
         *  remenberMe()
         */
        //WebSecurityExpressionRoot
        http.csrf().disable();
        //配置权限校验
        http.authorizeRequests()
        .antMatchers("/static/**", "/failure", "/403", "/login-register/**",
        "/group-chat/**","/open-course/**","/pay/**","/footer","/main",
        "/toefl-people/**","/welfare-castle/**","/index","/").permitAll()
        //.antMatchers("/toLogin").anonymous()
        //@beanName 可以获取Spring容器中的对象
        //@beanName.methodName() 可以获取对象的方法
        // 方法参数要传递的对象，可以在 WebSecurityExpressionRoot 中查看，是这个类型中的所有属性  下面方法不完善
        //.antMatchers("/**").access("@myAuthorityPermitImp.hasAuthority(request,authentication)") //使用自定义权限校验
        //基于资源的权限管理
        //.antMatchers("/admin/read").hasAuthority("admin:read")  //拥有admin:read权限的用户可以访问
        //.antMatchers("/admin/write").access("hasAuthority('admin:write')")
        //.antMatchers("/guest/read").access("hasAnyAuthority('admin:read','guest:read')")
        //.antMatchers("/guest/write").access("hasAnyAuthority('admin:write','guest:write')")
        //基于角色的权限管理 粗粒度
        .antMatchers("/admin/**").hasRole("管理员")
        //.antMatchers("/guest/read").hasAnyRole("访客","管理员")
        //.antMatchers("/guest/write").hasRole("访客")
        //.anyRequest().authenticated();// 任意请求，都必须校验
          .antMatchers("/admin/**","/personal-data/user-index").authenticated();

        http.formLogin().loginPage("/login")
        .loginProcessingUrl("/tologin") //处理用户登录逻辑的地址 默认/login 默认返回成功页面是根路径"/"
        //.successHandler(new MyAuthenticationSuccessHandler("/index", true))//自定义登录成功跳转地址
                .successHandler(new AuthenticationSuccessHandler() {
                    @Override
                    public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
                        User user = userMapper.selectUserByUserName(authentication.getName());
                        String urls = "";
                        switch (user.getRoleId()){
                            case 1:
                                urls = "/admin/login";
                                break;
                            default:
                                urls="/";
                                break;
                        }
                        httpServletResponse.sendRedirect(urls);
                    }
                })
        .failureHandler(new MyAuthenticationFailureHandler("/failure", false));
        //.defaultSuccessUrl("/toMain",true);  //相应重定向，防止重复请求
        //.successForwardUrl("/toMain"); //用户成功登录后请求转发的地址，必须能请求post
        //http.exceptionHandling()
        //    .accessDeniedHandler(myAccessDeniedHandler);//配置403 异常
        http.rememberMe()
                .rememberMeParameter("remember-me") //默认是 remember-me
                .tokenValiditySeconds(60 * 60 * 24 * 7) //单位秒 默认7天
                .rememberMeCookieName("remember-me")  //修改cookie名称 默认remember-me
                .tokenRepository(persistentTokenRepository)
                .userDetailsService(securityLoginUser);
        http.logout()
                .invalidateHttpSession(true) //回收 session对象 默认值都是 true
                .clearAuthentication(true) // 退出之前 清空 security 记录的 用户登录标记  默认值就是true
                //.addLogoutHandler()   //增加退出处理器
                //.logoutSuccessUrl("/toLogin")
                .logoutUrl("/logout"); // 默认就是logout
        http.headers().frameOptions().disable();
    }

    /**
     * 创建一个持久化用户登录标记的repository对象
     * Security 会自动创建表格
     *
     * @return
     */
    @Bean
    public PersistentTokenRepository persistentTokenRepository(DataSource dataSource) {
        JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
        if (dataSource != null) {
            jdbcTokenRepository.setDataSource(dataSource);
            //设置启动的时候，只有数据库不存在表格时 ，才创建， 默认值是false 当数据库存在的时候 可以设置为false
            //jdbcTokenRepository.setCreateTableOnStartup(true);
        }
        return jdbcTokenRepository;
    }


    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(securityLoginUser).passwordEncoder(bCryptPasswordEncoder());
    }
}

